package com.abc.scholarship.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.abc.scholarship.common.enums.ApplyEnums;
import com.abc.scholarship.common.utils.BeanUtils;
import com.abc.scholarship.common.utils.UserHolder;
import com.abc.scholarship.domain.dto.PlusesDTO;
import com.abc.scholarship.domain.entity.Apply;
import com.abc.scholarship.domain.entity.Student;
import com.abc.scholarship.domain.page.PageDTO;
import com.abc.scholarship.domain.page.PageQuery;
import com.abc.scholarship.domain.vo.resp.ApplyVO;
import com.abc.scholarship.exception.BusinessException;
import com.abc.scholarship.mapper.ApplyMapper;
import com.abc.scholarship.service.ApplyService;
import com.abc.scholarship.service.StudentService;
import com.abc.scholarship.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 申请表 服务实现类
 * </p>
 *
 * @author jzw
 * @since 2024-07-02
 */
@Service
public class ApplyServiceImpl extends ServiceImpl<ApplyMapper, Apply> implements ApplyService {

    @Autowired
    private StudentService studentService;

    @Autowired
    private ApplyMapper applyMapper;

    @Override
    @Transactional
    public void examineApply(Long applyId, Integer status) {
        // 修改申请状态
        boolean isTrue = update(new LambdaUpdateWrapper<Apply>()
                .eq(Apply::getId, applyId)
                .eq(Apply::getStatus, ApplyEnums.DEFAULT.getType())
                .set(Apply::getStatus, status)
        );

        // 申请成功
        if (isTrue && ApplyEnums.SUCCESS.getType().equals(status)) {
            ((ApplyServiceImpl) (AopContext.currentProxy())).addStuGpa(applyId);
        }
    }

    @Async
    public void addStuGpa(Long applyId) {
        Apply apply = getById(applyId);
        // 修改学生的绩点
        studentService.update(new LambdaUpdateWrapper<Student>()
                .eq(Student::getUId, apply.getUId())
                .set(Student::getAddGpa, apply.getStuAddGpa())
                .set(Student::getTotalGpa, apply.getStuTotalGpa())
        );
    }

    @Override
    public void addApply(List<PlusesDTO> plusesDTOList) {
        //TODO 这里json类型不知道有问题没有，等前端写完再进行测试  ---- HeYongL
        BigDecimal sumGpa = new BigDecimal(0);
        String pluses = JSONUtil.toJsonStr(plusesDTOList);
//        List<PlusesDTO> plusesDTOList = JSONUtil.toList(pluses, PlusesDTO.class);
        for (PlusesDTO plusesDTO : plusesDTOList) {
            sumGpa = sumGpa.add(BigDecimal.valueOf(plusesDTO.getGpa()));
        }


        // TODO 用户ID获取，还没写用户登录，无法获取 --- HeYongL
        Long userId = UserHolder.getUId();
        //判断今年是否已经申请
        if (checkIsApply(userId, LocalDate.now().getYear())) {
            throw new BusinessException("今年已经申请了");
        }
        //未申请
        Student student = studentService.getById(userId);
        Apply apply = new Apply();
        apply.setPluses(pluses);
        apply.setUId(userId);
        apply.setStuGpa(student.getGpa());
        apply.setStuAddGpa(sumGpa);
        apply.setStuTotalGpa(sumGpa.add(student.getGpa()));
        apply.setYear(LocalDate.now().getYear());
        baseMapper.insert(apply);
    }

    @Override
    public void updateApply(List<PlusesDTO> plusesDTOList, Long applyId) {
        // 检查是否以及申请
        Integer year = LocalDate.now().getYear();
        Long userId = UserHolder.getUId();
        Apply apply = lambdaQuery()
                .eq(Apply::getUId, userId)
                .eq(Apply::getYear, year)
                .one();
        if (Objects.isNull(apply)) {
            throw new BusinessException("申请表不存在");
        }
        // 更新
        BigDecimal sumGpa = new BigDecimal(0);
        for (PlusesDTO plusesDTO : plusesDTOList) {
            sumGpa = sumGpa.add(BigDecimal.valueOf(plusesDTO.getGpa()));
        }
        apply.setPluses(JSONUtil.toJsonStr(plusesDTOList));
        apply.setStuAddGpa(sumGpa);
        apply.setStuTotalGpa(sumGpa.add(apply.getStuGpa()));
        apply.setStatus(ApplyEnums.DEFAULT.getType());
        updateById(apply);
    }

    @Override
    public PageDTO<ApplyVO> stuQueryApply(PageQuery query) {
        Long uId = UserHolder.getUId();
        List<ApplyVO> page = applyMapper.getPage(
                (query.getPageNo() - 1) * query.getPageSize(), query.getPageSize(), uId);
        Long total = applyMapper.selectCount(new LambdaUpdateWrapper<Apply>()
                .eq(Apply::getUId, uId));
        return PageDTO.<ApplyVO>builder()
                .total(total)
                .pages((long) Math.ceil(total * 1.0 / query.getPageSize()))
                .list(page.stream().map(this::setPluses).collect(Collectors.toList()))
                .build();
    }

    /**
     * 判断今年是否已经提交申请
     *
     * @param userId
     * @param year
     * @return
     */
    private boolean checkIsApply(Long userId, Integer year) {
        Long count = lambdaQuery()
                .eq(Apply::getUId, userId)
                .eq(Apply::getYear, year)
                .count();
        return count > 0;
    }

    @Override
    public PageDTO<ApplyVO> queryApply(PageQuery query) {
        List<ApplyVO> page = applyMapper.getPage(
                (query.getPageNo() - 1) * query.getPageSize(), query.getPageSize(), null);
        Long total = applyMapper.selectCount(new LambdaUpdateWrapper<>());
        return PageDTO.<ApplyVO>builder()
                .total(total)
                .pages((long) Math.ceil(total * 1.0 / query.getPageSize()))
                .list(page.stream().map(this::setPluses).collect(Collectors.toList()))
                .build();
    }

    public ApplyVO setPluses(ApplyVO item){
        List<PlusesDTO> plusesDTO = JSONUtil.toList(item.getPluses(), PlusesDTO.class);
        item.setPlusesList(plusesDTO);
        return item;
    }
}
